/*
@Time : 2018/11/8 下午7:12 
@Author : baoye
@File : blockchain
@Software: GoLand
@brief: 
*/

package main

import (
	"github.com/boltdb/bolt"
	"fmt"
	"os"
)

type BlockChain struct {
	// 操作数据库的句柄
	db *bolt.DB

	// 存储最后一个区块的哈希
	tail []byte
}

const BlockChainDB = "BlockChainDB"
const BucketName = "Bucket"
const LashHash = "LashHash"

func NewBlockChain(address string) *BlockChain {

	if dbExists() {
		fmt.Printf("blockchain already exist!")
		os.Exit(1)
	}

	var lastHash []byte
	// 打开数据库
	db, err := bolt.Open(BlockChainDB, 0600, nil)
	if err != nil {
		fmt.Println("打开数据库失败！")
		os.Exit(1)
	}

	// 找到抽屉
	db.Update(func(tx *bolt.Tx) error {
		//bucket := tx.Bucket([]byte(BucketName))
		// 创建抽屉
		bucket, err := tx.CreateBucket([]byte(BucketName))
		if err != nil {
			fmt.Println("err:", err)
			os.Exit(1)
		}

		coinbaseTx := NewCoinbaseTX(address, genesisInfo)
		genesisBlock := NewBlock([]*Transaction{coinbaseTx}, nil)
		bucket.Put(genesisBlock.Hash, genesisBlock.Serialize())
		bucket.Put([]byte(LashHash), genesisBlock.Hash)

		lastHash = genesisBlock.Hash

		return nil
	})

	return &BlockChain{db, lastHash}
}

func GetBlockChainObj()*BlockChain  {

	if !dbExists() {
		fmt.Println("blockchain not exist, pls create first!")
		os.Exit(1)
	}

	var lastHash []byte
	db, err := bolt.Open(BlockChainDB,0600,nil)
	if err != nil {
		fmt.Println("打开数据库失败！")
		os.Exit(1)
	}

	db.View(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(BucketName))
		if bucket == nil {
			fmt.Printf("bucket should not be nil!")
			os.Exit(1)
		}
		lastHash = bucket.Get([]byte(LashHash))
		return nil
	})

	return &BlockChain{db, lastHash}
}



func (bc *BlockChain) AddBlock(txs []*Transaction) {

	// 获取最新区块
	lastHash := bc.tail

	// 创建新区快
	newBlock := NewBlock(txs, lastHash)

	// 将区块添加到区块链中
	bc.db.Update(func(tx *bolt.Tx) error {
		bucket := tx.Bucket([]byte(BucketName))
		if bucket == nil {
			fmt.Println("bucket should not be nil!!")
			os.Exit(1)
		} else {
			// 添加区块
			bucket.Put(newBlock.Hash, newBlock.Serialize())
			bucket.Put([]byte(LashHash), newBlock.Hash)

			bc.tail = newBlock.Hash
		}
		return nil
	})
}

func dbExists() bool  {
	if _,err := os.Stat(BlockChainDB);os.IsNotExist(err) {
		return false
	}
	return true
}